using System;
using System.Collections;
using System.Data;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Globalization;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Policy;
using PHSRAG.Insight21.SearchContext;
using PHSRAG.Security.Enumerations;
using PHSRAG.Utility;
using AgreementsConstants = PHSRAG.Insight21.InsightUtilities.Constants.AgreementsConstants;
using UserContext = PHSRAG.Insight21.Context.UserContext;
using AgreementDetailsSubNavigation = PHSRAG.Insight21.InsightWebControls.AgreementDetailsSubNavigation;
using Constants = PHSRAG.Insight21.InsightUtilities.Constants;
using Helper = PHSRAG.WebControls.Helper;

namespace PHSRAG.Insight21.Agreements
{
	/// <summary>
	/// The Projections Detail page provides forecasts to Investigators on the Projected Grant
	/// Balance at the end of the budget period based on the budget, expense and Personnel Data.
	/// This screen helps the users to identify in advance grant surpluses or deficits and take
	/// the necessary corrective action.
	/// In addition page supports calulcation of projected balance based on the user input of 
	/// additional amount and account category type, download to pdf, excel format and send by
	/// email.
	/// </summary>
	public partial class AgreementProjections : NoCachePage
	{
		#region Constants

	    private const string FundNumberKey = "FundNumberKey_deab9152-370e-4f9c-8577-c612f835e960";
	    private const string ClosingDateKey = "ClosingDateKey_deab9152-370e-4f9c-8577-c612f835e960";
	    private const string ClickButtonScriptKey = "clickButtonScript_601AE4D7-2CBB-413c-82AF-789A94B00BBF";
	    private const int DefaultAgreementId = 0;
	    private const int FirstRowIndex = 0;
	    private const int FirstChildControl = 1;
	    private const int AdditionalAmountCell = 7;
	    private const int CategoryIDCell = 1;
	    private const int AccountNumberCell = 3;
	    private const int RadioButtonCell = 4;

	    // the variables below (ExpandedTag, StateColName) need to be in sync with
	    // eponymous constants defined in ExpandingDataGrid control
	    private const string ExpandedTag = "--Expanded--";
	    private const string StateColName = "StateColumn";

	    private const string DefaultAmount = "0";
	    private const string CategoryLabelID = "category";
	    private const string AccountTypeRadioButtonListID = "accountType";
	    private const string BudgetForCurrentPeriodLabelID = "budgetForCurrentPeriod";
	    private const string ExpenseToDateLabelID = "expenseToDate";
	    private const string AdditionalAmountTextBoxID = "additionalAmount";
	    private const string EncumbrancesLabelID = "encumbrances";
	    private const string BalanceLabelID = "balance";
	    private const string EstimatedExpensesLabelID = "estimatedExpenses";

	    private const string DirectBudgetLabelID = "directBudget";
	    private const string DirectExpenseLabelID = "directExpense";
	    private const string DirectEncumbrancesLabelID = "directEncumbrances";
	    private const string DirectBalanceLabelID = "directBalance";
	    private const string DirectEstimatedExpensesLabelID = "directEstimatedExpenses";

	    private const string IndirectBudgetLabelID = "indirectBudget";
	    private const string IndirectExpenseLabelID = "indirectExpense";
	    private const string IndirectEncumbrancesLabelID = "indirectEncumbrances";
	    private const string IndirectBalanceLabelID = "indirectBalance";
	    private const string IndirectEstimatedExpensesLabelID = "indirectEstimatedExpenses";

	    private const string TotalBudgetLabelID = "totalBudget";
	    private const string TotalExpenseLabelID = "totalExpense";
	    private const string TotalEncumbrancesLabelID = "totalEncumbrances";
	    private const string TotalBalanceLabelID = "totalBalance";
	    private const string TotalEstimatedExpensesLabelID = "totalEstimatedExpenses";

	    // for revenue, prior yr blnc and cash
	    private const string RevenueLabelID = "revenue";
	    private const string PriorYearBalanceLabelID = "priorYearBalance";
	    private const string CashBalanceLabelID = "cashBalance";
	    private const string AdditionalCashBalanceLabelID = "additionalCashBalance";

	    // Account Type Column Name, needed in download to excel
	    private const string AccountTypeValueColName = "AccountTypeValue";

	    // for PersonnelForCurrentBudget controls
	    private const string PersonnelForCurrentBudgetLink = "personnelForCurrentBudgetLink";
	    private const string PersonnelForCurrentBudgetTitle = "personnelForCurrentBudgetTitle";

		#endregion

		#region Enumerations (Private)
		
        /// <summary>
        /// must be in sync with columns in datagrid
        /// </summary>
		private enum PersonnelGridColumns
		{
			Name,
			ProjectSalary,
			MonthlyRate,
			MonthsRemaining,
			CommittedSalary,
			FringeRate,
			CommittedFringe,
			TotalCommitted
		}

        /// <summary>
		/// Enumerates the account type being displayed in the 
		/// radio button list at the account (detail) level
        /// </summary>
		private enum RadioButtonListItems
		{
			Linear,
			NonLinear
		}
		#endregion

	    #region Instance Variables (Private)

	    private ICachePolicy cachePolicy;
	    private ICachePolicy pageCachePolicy;
	    private decimal monthsRemainingInBudgetPeriod;

	    #endregion

	    #region Private Properties

	    /// <summary>
	    /// Gets the Projections Details object with its data loaded.
	    /// This property gets the Projections Details object from the cache and checks if the object
	    /// is null. If yes an instance of projections details is created and the object is loaded with
	    /// data using the FundNumber and the closing date. The projections object is then cached and returned.
	    /// </summary>
	    private Projections ProjectionsDetails
	    {
	        get
	        {
	            Projections projections = ResultsCache.GetObject(cachePolicy, Projections.CacheKey) as Projections;
	            if (projections == null)
	            {
	                projections = new Projections();
	                UserContext userContext = cachePolicy.GetCachedObject(UserContext.CacheKey) as UserContext;
	                projections.Load(DataAccess.GetConnectionString(),
	                    userContext.DomainUserId, FundNumber, ClosingDate);
	                ResultsCache.SetObject(cachePolicy, Projections.CacheKey, projections);
	            }
	            return projections;
	        }
	    }

	    /// <summary>
	    /// Gets and sets the closing date used for loading the projections report. 
	    /// Returns short string value of current system date if the value fetched 
	    /// from the pagecache policy is null.
	    /// </summary>
	    private string ClosingDate
	    {
	        get
	        {
	            Hashtable h = pageCachePolicy.GetCachedObject(Keys.ProjectionsDetailsPageKey) as Hashtable;
	            return (h == null || h[ClosingDateKey] == null) ? DateTime.Now.ToShortDateString() : (string) h[ClosingDateKey];
	        }
	        set
	        {
	            Hashtable h = pageCachePolicy.GetCachedObject(Keys.ProjectionsDetailsPageKey) as Hashtable ?? new Hashtable();
	            h[ClosingDateKey] = value;
	            pageCachePolicy.Cache(Keys.ProjectionsDetailsPageKey, h);
	        }
	    }

	    /// <summary>
	    /// Gets and sets the fund number used for loading the projections report. 
	    /// Returns an empty string if the value fetched from the pagecache policy is null.
	    /// </summary>
	    private string FundNumber
	    {
	        get
	        {
	            Hashtable h = pageCachePolicy.GetCachedObject(Keys.ProjectionsDetailsPageKey) as Hashtable;
	            return (h == null || h[FundNumberKey] == null) ? string.Empty : (string) h[FundNumberKey];
	        }
	        set
	        {
	            Hashtable h = pageCachePolicy.GetCachedObject(Keys.ProjectionsDetailsPageKey) as Hashtable ?? new Hashtable();
	            h[FundNumberKey] = value;
	            pageCachePolicy.Cache(Keys.ProjectionsDetailsPageKey, h);
	        }
	    }

	    /// <summary>
	    /// Gets whether or not user can Run Projections. 
	    /// If the months remaining in project is 0 or less, then this property would return false
	    /// </summary>
	    private bool CanRunProjections
	    {
	        get { return !(Convert.ToDecimal(projectHeaderBar.MonthsRemainingInProject) <= 0); }
	    }

	    #endregion

	    #region Events

	    /// <summary>
	    /// Purges the cache and sets the correct search context level before the breadcrumb performs a redirect.
	    /// </summary>
	    /// <param name="sender"></param>
	    /// <param name="e"></param>
	    protected void OnNavigate(object sender, CommandEventArgs e)
	    {
	        if (((CommandEventArgs) (e.CommandArgument)).CommandName == Convert.ToString((int) SearchCriteria.Level.Summary))
	        {
	            //set the search context's level to the correct level
	            CommonSearchContext searchContext = (CommonSearchContext) cachePolicy.GetCachedObject(CommonSearchContext.CacheKey);
	            searchContext.CurrentLevel = SearchCriteria.Level.Summary;
	        }

	        PurgeCache();
	    }

	    #region Overrides

	    protected override void OnPreRender(EventArgs e)
	    {
	        if (!Page.ClientScript.IsClientScriptBlockRegistered(ClickButtonScriptKey))
	        {
	            string script = string.Format(
                    "<script language=\"javascript\">\n" +
                    "function ClickButton(e, buttonid){{\n" +
                    "\tvar evt = e ? e : window.event;\n" +
                    "\tvar bt = document.getElementById(buttonid);\n" +
                    "\tif (typeof bt == 'object'){{\n" +
                    "\t\tif (evt.keyCode == 13){{\n" +
                    "\t\t\tbt.click();\n" +
                    "\t\t\treturn false;\n" +
                    "\t\t}}\n" +
                    "\t}}\n" +
                    "}}\n" +
                    "</script>\n");

	            Page.ClientScript.RegisterClientScriptBlock(GetType(), ClickButtonScriptKey, script);
	        }

	        base.OnPreRender(e);
	    }

	    #endregion

	    #region Page Load

	    /// <summary>
	    /// Handler for the event raised when the page is being loaded. 
	    /// When the page is loaded and is not a post back the following sequence of events happen:
	    /// <list type="bullet">
	    /// <item><description>The projections object is removed from the cache if present. 
	    /// This is done so that the expanding grid displaying the category and account 
	    /// information displays correct set of data when the user goes back to parent page
	    /// using the breadcrumb and navigates back to the page by clicking on the previous value.</description></item>
	    /// <item><description>Its checked if the incoming data for this page contains the relevant 
	    /// data for loading this page, if not it raises an exception. </description></item>
	    /// <item><description>Load the project header bar using the passed fund number</description></item>
	    /// <item><description>Load the controls on the page with data</description></item>
	    /// </list>
	    /// </summary>
	    /// <param name="sender">Control which raised the event</param>
	    /// <param name="e">Arguments associated with the event</param>
	    protected void Page_Load(object sender, EventArgs e)
	    {
	        downloadbar.SQLReportName = "Agmt_Projections";
	        annunciator.Message = string.Empty;
	        annunciator.Title = string.Empty;
	        breadCrumb.Add(@"Projections", Settings.GetKeyValue(@"SubNavigation.AgreementDetails.ProjectionsURL", string.Empty), Settings.GetKeyValue
	            (@"BreadCrumb.DetailPageLevel", AgreementConstants.DetailPageLevelValue));

	        if (!IsPostBack)
	        {
	            displaySection.Visible = false;
	            ResultsCache.PurgeObject(cachePolicy);

	            try
	            {
	                bool showPersonnelForCurrentBudget = false;
	                Hashtable incomingHashTable = (Hashtable) cachePolicy.GetCachedObject(AgreementsConstants.AgreementDetailsKey);
	                if (incomingHashTable != null)
	                {
	                    if (incomingHashTable[Keys.JournalEndDateKey] == null || incomingHashTable[Keys.FundNumberKey] == null)
	                    {
	                        throw (new Exception("Project Details requires Fund Number and Closing Date"));
	                    }

	                    string fundNumber = incomingHashTable[Keys.FundNumberKey] as String;
	                    if (string.IsNullOrEmpty(fundNumber))
	                    {
	                        throw new Exception("Cannot load contract deatils without a valid fund number");
	                    }

	                    Hashtable h = cachePolicy.GetCachedObject(AgreementsConstants.AgreementDetailsKey) as Hashtable;
	                    int accessRights = (incomingHashTable[AgreementsConstants.AccessRightsKey] != null) ? Convert.ToInt32(h[AgreementsConstants.AccessRightsKey]) : 0;
	                    showPersonnelForCurrentBudget = ((accessRights & (int) AccessRightBit.ViewSalaryInformation) != 0);

	                    FundNumber = fundNumber;
	                    ClosingDate = (string) incomingHashTable[Keys.JournalEndDateKey];

	                    projectHeaderBar.LoadProjectHeader(fundNumber, 0, (int) incomingHashTable[Keys.BudgetYearKey]);
	                    h[Keys.RefreshBudgetDetailDataKey] = true;
	                    pageCachePolicy.Cache(AgreementsConstants.AgreementDetailsKey, h);
	                    LoadProjections();

	                    monthsRemaining.Text = monthsRemainingInBudgetPeriod.ToString();

	                    if (!CanRunProjections)
	                    {
	                        runProjections.Visible = false;
	                    }
	                }

	                displaySection.Visible = true;

	                if (showPersonnelForCurrentBudget)
	                {
	                    personnelForCurrentBudget.Visible = true;
	                }
	                else
	                {
	                    personnelForCurrentBudget.Visible = false;
	                    const string scriptBlockKey = "ProjectionsScript";

	                    if (!Page.ClientScript.IsStartupScriptRegistered(scriptBlockKey))
	                    {
	                        Page.ClientScript.RegisterStartupScript(GetType(), scriptBlockKey, string.Format(
                                "<script language='javascript'>\n" +
                                "function HidePersonnel()\n" +
                                "{{\n" +
                                "\tvar t = document.getElementById('{0}');\n" +
                                "\tif (t != null) {{ t.style.visibility = 'visible'; t.style.display = 'none'; }}\n" +
                                "\tt = document.getElementById('{1}');\n" +
                                "\tif (t != null) {{ t.style.visibility = 'visible'; t.style.display = 'none'; }}\n" +
                                "}}\n\n" +
                                "HidePersonnel();\n" +
                                "</script>\n", PersonnelForCurrentBudgetLink, PersonnelForCurrentBudgetTitle));
	                    }
	                }

	                agreementDetailsSubNavigation.SelectedLink = AgreementDetailsSubNavigation.ProjectionsCommand;
	            }
	            catch (Exception ex)
	            {
	                annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
	            }
	        }
	    }

	    #endregion

	    #region Projection Details Expanding DataGrid Events

	    /// <summary>
	    /// Handler for event raised when an item is data bound to ProjectionsDetails expanding 
	    /// data grid. While binding data check if the amount field has negative values then assign a 
	    /// style for displaying negative values. This formatting  is applied to Item types, alternating
	    /// Item types and the footer type. This event checks if the dataitem for the row is parent row or
	    /// child row. If the row is child row radio button displaying the account categories and text box
	    /// to take user input for additional amount is made visible.
	    /// </summary>
        /// <param name="sender">Control which raised the event</param>
	    /// <param name="e">Argument associated with the event</param>
	    protected void OnProjectionsDetailsItemDataBound(object sender, DataGridItemEventArgs e)
	    {
	        try
	        {
	            DataGridItem item = e.Item;

	            if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem)
	            {
	                DataRowView row = (DataRowView) item.DataItem;

	                ((Label) item.FindControl(CategoryLabelID)).Text =
	                    row[AgreementConstants.ExpenseCategoryColName].ToString();

	                FormatCurrencyValue(item.FindControl(BudgetForCurrentPeriodLabelID), row[AgreementConstants.BudgetForCurrentPeriodColName]);
	                FormatCurrencyValue(item.FindControl(ExpenseToDateLabelID), row[AgreementConstants.ExpenseToDateColName]);

	                FormatCurrencyValue(item.FindControl(EncumbrancesLabelID), row[AgreementConstants.EncumbrancesColName]);
	                FormatCurrencyValue(item.FindControl(BalanceLabelID), row[AgreementConstants.BalanceColName]);
	                FormatCurrencyValue(item.FindControl(EstimatedExpensesLabelID), row[AgreementConstants.EstimatedExpensesColName]);

	                if (row[StateColName] != DBNull.Value && row[StateColName].ToString() != ExpandedTag)
	                {
	                    RadioButtonList accountType = (RadioButtonList) item.FindControl(AccountTypeRadioButtonListID);

	                    if (item.ItemType == ListItemType.Item)
	                    {
	                        item.CssClass = AgreementConstants.GridItemExpanded;
	                        accountType.CssClass = AgreementConstants.InnerRadioButton;
	                    }
	                    else
	                    {
	                        item.CssClass = AgreementConstants.GridAltItemExpanded;
	                        accountType.CssClass = AgreementConstants.InnerRadioButtonExpanded;
	                    }

	                    if (CanRunProjections)
	                    {
	                        accountType.Visible = true;
	                        accountType.SelectedIndex = Convert.ToBoolean(row[AgreementConstants.AccountTypeColName]) ?
	                                                                                                                      (int) RadioButtonListItems.Linear : (int) RadioButtonListItems.NonLinear;
	                        TextBox additionalAmount = (TextBox) item.FindControl(AdditionalAmountTextBoxID);
	                        additionalAmount.Visible = true;
	                        FormatCurrencyValue(additionalAmount, row[AgreementConstants.AdditionalAmountColName]);
	                    }
	                }
	            }
	            else if (item.ItemType == ListItemType.Footer)
	            {
	                DataSet projections = ProjectionsDetails.GetDataContainer();

	                DataTable directCost = projections.Tables[AgreementsConstants.DirectCostsTableName];

	                if (directCost.Rows.Count > 0)
	                {
	                    DataRow directCostRow = directCost.Rows[FirstRowIndex];

	                    FormatCurrencyValue(item.FindControl(DirectBudgetLabelID), directCostRow[AgreementConstants.BudgetForCurrentPeriodColName]);
	                    FormatCurrencyValue(item.FindControl(DirectExpenseLabelID), directCostRow[AgreementConstants.ExpenseToDateColName]);
	                    FormatCurrencyValue(item.FindControl(DirectEncumbrancesLabelID), directCostRow[AgreementConstants.EncumbrancesColName]);
	                    FormatCurrencyValue(item.FindControl(DirectBalanceLabelID), directCostRow[AgreementConstants.BalanceColName]);
	                    FormatCurrencyValue(item.FindControl(DirectEstimatedExpensesLabelID), directCostRow[AgreementConstants.EstimatedExpensesColName]);
	                }

	                DataTable indirectCost = projections.Tables[AgreementsConstants.IndirectCostsTableName];

	                if (indirectCost.Rows.Count > 0)
	                {
	                    DataRow indirectCostRow = indirectCost.Rows[FirstRowIndex];

	                    FormatCurrencyValue(item.FindControl(IndirectBudgetLabelID), indirectCostRow[AgreementConstants.BudgetForCurrentPeriodColName]);
	                    FormatCurrencyValue(item.FindControl(IndirectExpenseLabelID), indirectCostRow[AgreementConstants.ExpenseToDateColName]);
	                    FormatCurrencyValue(item.FindControl(IndirectEncumbrancesLabelID), indirectCostRow[AgreementConstants.EncumbrancesColName]);
	                    FormatCurrencyValue(item.FindControl(IndirectBalanceLabelID), indirectCostRow[AgreementConstants.BalanceColName]);
	                    FormatCurrencyValue(item.FindControl(IndirectEstimatedExpensesLabelID), indirectCostRow[AgreementConstants.EstimatedExpensesColName]);
	                }

	                DataTable totalCost = projections.Tables[AgreementsConstants.TotalCostsTableName];

	                if (totalCost.Rows.Count > 0)
	                {
	                    DataRow totalCostRow = projections.Tables[AgreementsConstants.TotalCostsTableName].Rows[FirstRowIndex];

	                    FormatCurrencyValue(item.FindControl(TotalBudgetLabelID), totalCostRow[AgreementConstants.BudgetForCurrentPeriodColName]);
	                    FormatCurrencyValue(item.FindControl(TotalExpenseLabelID), totalCostRow[AgreementConstants.ExpenseToDateColName]);
	                    FormatCurrencyValue(item.FindControl(TotalEncumbrancesLabelID), totalCostRow[AgreementConstants.EncumbrancesColName]);
	                    FormatCurrencyValue(item.FindControl(TotalBalanceLabelID), totalCostRow[AgreementConstants.BalanceColName]);
	                    FormatCurrencyValue(item.FindControl(TotalEstimatedExpensesLabelID), totalCostRow[AgreementConstants.EstimatedExpensesColName]);
	                }

	                DataTable revenue = projections.Tables[AgreementsConstants.RevenueTableName];

	                if (revenue.Rows.Count > 0)
	                {
	                    FormatCurrencyValue(item.FindControl(RevenueLabelID), revenue.Rows[FirstRowIndex][AgreementConstants.RevenueColName]);
	                }

	                DataTable priorYearBalance = projections.Tables[AgreementsConstants.PriorYrBalanceTableName];

	                if (priorYearBalance.Rows.Count > 0)
	                {
	                    FormatCurrencyValue(item.FindControl(PriorYearBalanceLabelID), priorYearBalance.Rows[FirstRowIndex][AgreementConstants.PriorYearBalanceColName]);
	                }

	                DataTable cashBalance = projections.Tables[AgreementsConstants.CashBalanceTableName];

	                if (cashBalance.Rows.Count > 0)
	                {
	                    FormatCurrencyValue(item.FindControl(CashBalanceLabelID), cashBalance.Rows[FirstRowIndex][AgreementConstants.CashBalanceColName]);
	                    FormatCurrencyValue(item.FindControl(AdditionalCashBalanceLabelID), cashBalance.Rows[FirstRowIndex][AgreementConstants.AdditionalCashBalanceColName]);
	                }
	            }
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
	        }
	    }

	    /// <summary>
	    /// Handler for the event raised when the collapsed symbol on the master row is clicked
	    /// to display related child records. This event selects the rows from the child table
	    /// which is related to the given key. The slected rows are inserted into a new table
	    /// and returned to the calling method.
	    /// </summary>
	    /// <param name="key">The categoryID which is used as the key</param>
	    /// <returns>table containing child records related to the given key</returns>
	    protected object OnProjectionsDetailsItemExpanded(string key)
	    {
	        DataTable childTable = ProjectionsDetails.GetDataContainer().Tables[AgreementsConstants.AccountsTableName];
	        DataRow[] filteredChildRows = childTable.Select(String.Format("{0} = {1}", AgreementConstants.SortOrderColName, key));
	        DataTable filteredChildTable = childTable.Clone();

	        foreach (DataRow row in filteredChildRows)
	        {
	            filteredChildTable.ImportRow(row);
	        }

	        return filteredChildTable;
	    }

	    #endregion

	    #region Personnel For Current Budget DataGrid Events

	    /// <summary>
	    /// Handler for event when any item in PersonnelForCurrentBudget Grid is created.
	    /// On Item Created event of the dataGrid is used to indicate the sort column and the sort order.
	    /// It references the properties on ProjectionsDetails for SortColumn and SortAscending and sets
	    /// the appropriate web ding in the appropriate column of the grid header as below.
	    /// <list type="bullet">
	    /// <item><description>Set the sort symbol text using the property of ProjectionsDetails object stored in cache</description></item>
	    /// <item><description>Create a Hashtable of column indices, keyed by the sort expression of each column</description></item>
	    /// <item><description>Using the hash table and SortColumn property on ProjectionsDetails object add a label 
	    /// with sort symbol to the column on which sort has happened</description></item>
	    /// </list>
	    /// </summary>
	    /// <param name="sender">Control which raised the event</param>
	    /// <param name="e">Arguments associated with the event</param>
	    protected void OnPersonnelForCurrentBudgetItemCreated(object sender, DataGridItemEventArgs e)
	    {
	        try
	        {
	            DataGridItem item = e.Item;

	            if (item.ItemType == ListItemType.Header)
	            {
	                Projections projections = ProjectionsDetails;

	                Label sortSymbol = new Label();
	                sortSymbol.Text = projections.PersonnelGridSortAscending ? Constants.SortAscendingText : Constants.SortDescendingText;
	                sortSymbol.Font.Name = Constants.SortSymbolFontName;

	                Hashtable columnMap = new Hashtable();

	                foreach (DataGridColumn column in personnelForCurrentBudget.Columns)
	                {
	                    if (column.SortExpression != string.Empty)
	                    {
	                        columnMap[column.SortExpression] = personnelForCurrentBudget.Columns.IndexOf(column);
	                    }
	                }

	                string sortColumn = projections.PersonnelGridSortColumn;

	                if (columnMap.ContainsKey(sortColumn))
	                {
	                    (item.Cells[(int) columnMap[sortColumn]]).Controls.Add(sortSymbol);
	                }
	            }
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
	        }
	    }

	    /// <summary>
	    /// Handler for event raised when an item is data bound to PersonnelForCurrentBudget grid. 
	    /// While binding data check if the amount field has negative values then assign a 
	    /// style for displaying negative values. This formatting  is applied to Item types, alternating
	    /// Item types and the footer type. 
	    /// /// </summary>
        /// <param name="sender">Control which raised the event</param>
	    /// <param name="e">Argument associated with the event</param>
	    protected void OnPersonnelForCurrentBudgetItemDataBound(object sender, DataGridItemEventArgs e)
	    {
	        try
	        {
	            DataGridItem item = e.Item;

	            if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem)
	            {
	                DataRowView personnelRow = (DataRowView) item.DataItem;

	                FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.ProjectSalary], personnelRow[AgreementConstants.ProjectSalaryColName]);
	                FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.MonthlyRate], personnelRow[AgreementConstants.MonthlyRateColName]);
	                FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.CommittedSalary], personnelRow[AgreementConstants.CommittedSalaryColName]);
	                FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.CommittedFringe], personnelRow[AgreementConstants.CommittedFringeColName]);
	                FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.TotalCommitted], personnelRow[AgreementConstants.TotalCommittedColName]);
	            }
	            else if (item.ItemType == ListItemType.Footer)
	            {
	                DataRowCollection personnelBudgetTotals = ProjectionsDetails.GetDataContainer().
	                    Tables[AgreementConstants.PersonnelBudgetTotalsTableName].Rows;

	                if (personnelBudgetTotals.Count > 0)
	                {
	                    DataRow personnelBudgetTotalsRow = personnelBudgetTotals[FirstRowIndex];

	                    FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.ProjectSalary], personnelBudgetTotalsRow[AgreementConstants.ProjectSalaryColName]);
	                    FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.MonthlyRate], personnelBudgetTotalsRow[AgreementConstants.MonthlyRateColName]);
	                    FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.CommittedSalary], personnelBudgetTotalsRow[AgreementConstants.CommittedSalaryColName]);
	                    FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.CommittedFringe], personnelBudgetTotalsRow[AgreementConstants.CommittedFringeColName]);
	                    FormatCurrencyValue(item.Cells[(int) PersonnelGridColumns.TotalCommitted], personnelBudgetTotalsRow[AgreementConstants.TotalCommittedColName]);
	                }
	            }
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
	        }
	    }

	    /// <summary>
	    /// Handler for event when any column on PersonnelForCurrentBudget is sorted.
	    /// This event handles sorting on the datagrid. It retrieves the projections object
	    /// from the cache and calls a method on it to sort the personnel data and set the 
	    /// datasource of the grid with the sorted data.
	    /// </summary>
	    /// <param name="source">Control which raised the event</param>
	    /// <param name="e">Arguments associated with the event</param>
	    protected void OnPersonnelForCurrentBudgetSortCommand(object source, DataGridSortCommandEventArgs e)
	    {
	        try
	        {
	            Projections projections = ProjectionsDetails;
	            personnelForCurrentBudget.DataSource = projections.SortPersonnelData(e.SortExpression);
	            personnelForCurrentBudget.DataBind();
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = String.Format("{0} Error in sorting Personnel For Current Budget.",
	                ExceptionReport.GetExceptionInfo(ex, false));
	        }
	    }

	    #endregion

	    #region Run Projections Event

	    /// <summary>
	    /// Handler for event fired on run projections click. It recalculates the 
	    /// estimated expenses through end of period, estimated ending balance, 
	    /// current obligations based on the values entered for additional amount and
	    /// account type selected. It creates hashtables for storing the values of
	    /// additional amount and the account type entered by the user and passes the
	    /// same to the projections object to recalculate the values. The recalculated
	    /// values in the account rows are updated in the expanded child rows of the
	    /// expanding data grid.
	    /// </summary>
	    /// <param name="sender"></param>
	    /// <param name="e"></param>
	    protected void OnRunProjections(object sender, ImageClickEventArgs e)
	    {
	        try
	        {
	            Hashtable additionalAmount = new Hashtable();
	            Hashtable accountType = new Hashtable();
	            LoadAdditionalAmountAndCategoryTypeHashTable(additionalAmount, accountType);
	            UpdateDetailRows();
	            ProjectionsDetails.CalculateProjectedBalance(additionalAmount, accountType);
	            LoadProjections();

	            if (!personnelForCurrentBudget.Visible)
	            {
	                const string scriptBlockKey = "ProjectionsScript";

	                if (!Page.ClientScript.IsStartupScriptRegistered(scriptBlockKey))
	                {
	                    Page.ClientScript.RegisterStartupScript(GetType(), scriptBlockKey, string.Format(
                            "<script language='javascript'>\n" +
                            "function HidePersonnel()\n" +
                            "{{\n" +
                            "\tvar t = document.getElementById('{0}');\n" +
                            "\tif (t != null) {{ t.style.visibility = 'visible'; t.style.display = 'none'; }}\n" +
                            "\tt = document.getElementById('{1}');\n" +
                            "\tif (t != null) {{ t.style.visibility = 'visible'; t.style.display = 'none'; }}\n" +
                            "}}\n\n" +
                            "HidePersonnel();\n" +
                            "</script>\n", PersonnelForCurrentBudgetLink, PersonnelForCurrentBudgetTitle));
	                }
	            }
	        }
	        catch (FormatException)
	        {
	            annunciator.Message = String.Format("Error in calculating projected balance.");
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = String.Format("{0} Error in calculating projected balance.",
	                ExceptionReport.GetExceptionInfo(ex, false));
	        }
	    }

	    #endregion

	    #region Downloads

	    /// <summary>
	    /// Invoked by the download bar when it catches an exception.
	    /// </summary>
	    /// <param name="sender">Control which raised the event</param>
	    /// <param name="e">Argument associated with the event</param>
	    protected void OnDownloadFailed(object sender, CommandEventArgs e)
	    {
	        annunciator.Title = "Following error(s) occurred while downloading";
	        annunciator.Message = ExceptionReport.GetExceptionInfo((Exception) e.CommandArgument, false);
	    }

	    /// <summary>
	    /// Handles Send By Email button clicks by preparing a MailMessage to send back to the 
	    /// InsightDownloadBar control
	    /// </summary>
	    /// <remarks>
	    /// This method generates the raw information needed for the InsightDownloadBar to process 
	    /// and send an e-mail with a link back to the page with the correct session settings.
	    /// This page stores Current Balance, Estimated Ending Balance, Current Obligations and their keys
	    /// into the database. 
	    /// </remarks>
        ///<param name="queryID">Last inserted value from the database</param>
	    ///<param name="summaryInfo">Summary information to be send</param>
	    ///<param name="webConfigURLKey">URL information to see the page</param>
	    public void OnSendByEmail(out string webConfigURLKey, out int queryID, out Hashtable summaryInfo)
	    {
	        try
	        {
	            webConfigURLKey = "SubNavigation.AgreementDetails.ProjectionsURL";
	            string query = String.Format("{0}={1}&{2}={3}", Keys.FundNumberKey, FundNumber,
	                Keys.JournalEndDateKey, ClosingDate);
	            queryID = Projections.GetQueryId(query);

	            DataTable projectionsTotals = ProjectionsDetails.GetDataContainer().Tables[AgreementConstants.ProjectionsTotalTableName];
	            summaryInfo = null;

	            if (projectionsTotals.Rows.Count > 0)
	            {
	                summaryInfo = new Hashtable();
	            }
	        }
	        catch (Exception ex)
	        {
	            webConfigURLKey = null;
	            queryID = -1;
	            summaryInfo = null;
	            annunciator.Message = String.Format("{0} Error while sending by email.",
	                ExceptionReport.GetExceptionInfo(ex, false));
	        }
	    }

	    #endregion

	    #region Header

	    /// <summary>
	    /// Resets the subnavigation selected link(s) before the 
	    /// header performs a redirect.
	    /// </summary>
	    /// <param name="sender">Control which rasied the event.</param>
	    /// <param name="e">Arguments associated with the event.</param>
	    protected void OnHeaderNavigate(object sender, CommandEventArgs e)
	    {
	        PurgeCache();
	        agreementsSubNavigation.Reset();
	        agreementDetailsSubNavigation.Reset();
	    }

	    /// <summary>
	    /// Purges the cache before the agreements subnavigation header performs a redirect.
	    /// </summary>
	    /// <param name="sender">Control which rasied the event.</param>
	    /// <param name="e">Arguments associated with the event.</param>
	    protected void OnSubNavigate(object sender, CommandEventArgs e)
	    {
	        PurgeCache();
	    }

	    /// <summary>
	    /// Purges the cache before the agreement details subnavigation header performs a redirect.
	    /// </summary>
	    /// <param name="sender">Control which rasied the event.</param>
	    /// <param name="e">Arguments associated with the event.</param>
	    protected void OnDetailsSubNavigate(object sender, CommandEventArgs e)
	    {
	        PurgeCache();
	    }

	    #endregion

	    #endregion

	    #region Private Methods

	    /// <summary>
	    /// Loads controls on the page with data. The labels displaying data for projections totals,
	    /// the expanded data grid displaying categories(master) and accounts(details) and the
	    /// personnelforCurrentBudget grid displaying personnel data are loaded with data.
	    /// </summary>
	    private void LoadProjections()
	    {
	        try
	        {
	            DataSet projections = ProjectionsDetails.GetDataContainer();
	            LoadProjectionsTotals(projections);

	            projectionsDetails.DataSource = projections.Tables[AgreementsConstants.CategoryTableName];
	            monthsRemainingInBudgetPeriod = projections.Tables[AgreementsConstants.MonthsRemainingTableName].Rows.Count != 0 ? Convert.ToDecimal(projections.Tables[AgreementsConstants.MonthsRemainingTableName].Rows[0]["MonthsRemaining"]) : 0;

	            projectionsDetails.DataBind();

	            if (projections.Tables.Contains(AgreementConstants.PersonnelBudgetTableName))
	            {
	                personnelForCurrentBudget.DataSource = projections.Tables[AgreementConstants.PersonnelBudgetTableName];
	                personnelForCurrentBudget.DataBind();
	            }
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = String.Format("{0} Error while loading projections.",
	                ExceptionReport.GetExceptionInfo(ex, false));
	        }
	    }

	    /// <summary>
	    /// Formats and sets the text property of the labels displaying projections totals values.
	    /// </summary>
	    /// <param name="projections">dataset containing projections details data</param>
	    private void LoadProjectionsTotals(DataSet projections)
	    {
	        DataTable projectionsTotals = projections.Tables[AgreementConstants.ProjectionsTotalTableName];

	        if (projectionsTotals.Rows.Count > 0)
	        {
	            DataRow projectionsTotalsRow = projectionsTotals.Rows[FirstRowIndex];
	            FormatCurrencyValue(currentBalance, projectionsTotalsRow[AgreementConstants.CurrentBalanceColName]);
	            FormatCurrencyValue(currentObligations, projectionsTotalsRow[AgreementConstants.CurrentObligationsColName]);
	            FormatCurrencyValue(estimatedEndingBalance, projectionsTotalsRow[AgreementConstants.EstimatedEndingBalanceColName]);
	        }
	    }

	    /// <summary>
	    /// Loads the user input of additional amount and account type values in hashtable, using
	    /// account number and category id as the key.
	    /// </summary>
	    /// <param name="additionalAmount">hashtable to store the addtional amount values entered by the user</param>
	    /// <param name="accountType">hashtable to store the account type selected by the user</param>
	    private void LoadAdditionalAmountAndCategoryTypeHashTable(Hashtable additionalAmount, Hashtable accountType)
	    {
	        DataGridItemCollection itemCollection = projectionsDetails.Items;

	        foreach (DataGridItem item in itemCollection)
	        {
	            if (item.Cells[AdditionalAmountCell].Controls[FirstChildControl].Visible)
	            {
	                string key = String.Format("{0}{1}", ((Label) item.Cells[AccountNumberCell].Controls
	                    [FirstChildControl]).Text, item.Cells[CategoryIDCell].Text);

	                accountType.Add(key, Convert.ToInt32(((RadioButtonList) item.Cells[RadioButtonCell].
	                    Controls[FirstChildControl]).SelectedValue));

	                string additionalAmountEntered = ((TextBox) item.Cells[AdditionalAmountCell].
	                    Controls[FirstChildControl]).Text.Replace(CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol,
	                        string.Empty);

	                additionalAmount.Add(key, Convert.ToDecimal(additionalAmountEntered != string.Empty ? additionalAmountEntered : DefaultAmount));
	            }
	        }
	    }

	    /// <summary>
	    /// Updates all the child rows in master table with the user input for the 
	    /// additional amount and the account category type as below:
	    /// <list type="bullet">
	    /// <item><description>Get the dataset from the cached object</description></item>
	    /// <item><description>Get the master and the detail table from the dataset</description></item>
	    /// <item><description>Loop through the detail rows(accounts) and retrieve the child(detail)
	    /// rows present in the master(category) table using the category id and account number(in name of expense category)
	    /// of the account row</description></item>
	    /// <item><description>If child rows are prsent in the parent table, update the cells whose value has changed after user input</description></item>
	    /// <item><description>Accept the changes on the dataset so that the updated rows are displayed correctly on the expanding data grid</description></item>
	    /// <item><description>Reset the expanding grid so that all cached objects are purged out</description></item>
	    /// </list>
	    /// </summary>
	    private void UpdateDetailRows()
	    {
	        try
	        {
	            DataSet projections = ProjectionsDetails.GetDataContainer();
	            DataTable category = projections.Tables[AgreementsConstants.CategoryTableName];
	            DataRowCollection accounts = projections.Tables[AgreementsConstants.AccountsTableName].Rows;

	            foreach (DataRow account in accounts)
	            {
	                DataRow[] expandedRows = category.Select(String.Format("{0} = {1} AND {2} = '{3}'",
	                    AgreementConstants.CategoryIDColName, account[AgreementConstants.CategoryIDColName],
	                    AgreementConstants.AccountIDColName, account[AgreementConstants.AccountIDColName]));

	                if (expandedRows.Length > 0)
	                {
	                    DataRow expandedRow = expandedRows[FirstRowIndex];
	                    expandedRow[AgreementConstants.AccountTypeColName] = account[AgreementConstants.AccountTypeColName];
	                    expandedRow[AgreementConstants.BudgetForCurrentPeriodColName] = account[AgreementConstants.BudgetForCurrentPeriodColName];
	                    expandedRow[AgreementConstants.ExpenseToDateColName] = account[AgreementConstants.ExpenseToDateColName];
	                    expandedRow[AgreementConstants.AdditionalAmountColName] = account[AgreementConstants.AdditionalAmountColName];
	                    expandedRow[AgreementConstants.EncumbrancesColName] = account[AgreementConstants.EncumbrancesColName];
	                    expandedRow[AgreementConstants.BalanceColName] = account[AgreementConstants.BalanceColName];
	                    expandedRow[AgreementConstants.EstimatedExpensesColName] = account[AgreementConstants.EstimatedExpensesColName];
	                }
	            }
	            projections.AcceptChanges();
	            projectionsDetails.Reset();
	        }
	        catch (Exception ex)
	        {
	            annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
	        }
	    }

	    /// <summary>
	    /// This method supports formatting and style setting for labels,
	    /// textboxes and table cells displaying currency values. It determines the
	    /// type of control and gets the value in the dataitem. Based on the value
	    /// of the dataitem, cssclass and text property of the control is set.
	    /// </summary>
	    /// <param name="control">control to be formatted</param>
	    /// <param name="dataItem">data to be displayed in the control</param>
	    private void FormatCurrencyValue(object control, object dataItem)
	    {
	        decimal itemValue = Math.Round(Convert.ToDecimal((dataItem != DBNull.Value) ? dataItem : DefaultAmount), 10);

	        if (control is Label)
	        {
	            Label item = (Label) control;
	            item.CssClass = itemValue < 0 ? AgreementConstants.ExpandedGridDeficitStyle : AgreementConstants.ExpandedGridCurrencyStyle;
	            item.Text = itemValue.ToString("c", Helper.ZeroDecimals());
	        }
	        else if (control is TextBox)
	        {
	            TextBox item = (TextBox) control;
	            item.CssClass = itemValue < 0 ? AgreementConstants.ExpandedGridDeficitStyle : AgreementConstants.ExpandedGridCurrencyStyle;
	            item.Text = itemValue.ToString("c", Helper.ZeroDecimals());
	        }
	        else if (control is TableCell)
	        {
	            TableCell item = (TableCell) control;
	            item.CssClass = itemValue < 0 ? AgreementConstants.DeficitStyle : AgreementConstants.CurrencyStyle;
	            item.Text = itemValue.ToString("c", Helper.ZeroDecimals());
	        }
	    }

	    /// <summary>
	    /// Get refresh dates to be passed down to reports.
	    /// </summary>
	    /// <returns>A string</returns>
	    private string GetDataRefreshDates()
	    {
	        StringBuilder dates = new StringBuilder();
	        dates.AppendFormat("Last GL Post| {0};  ", refreshDates.GLPostAsOfDate.ToShortDateString());
	        dates.AppendFormat("Last PO/AP Post| {0};  ", refreshDates.POAPAsOfDate.ToShortDateString());
	        dates.AppendFormat("Last Salary Post| {0};", refreshDates.SalaryAsOfDate.ToShortDateString());
	        return dates.ToString();
	    }

	    /// <summary>
	    /// Uncaches all objects with data for this page.
	    /// </summary>
	    private void PurgeCache()
	    {
	        ResultsCache.PurgeObject(cachePolicy);
	    }

	    /// <summary>
	    /// Adds refresh dates currently visible on the page to the search criteria string
	    /// to be passed down to reports. The criteria does not persist to the SearchCriteria
	    /// object.
	    /// </summary>
	    /// <param name="searchCriteria">Object containing search criteria.</param>
	    /// <returns>A string representation of search criteria. </returns>
	    private string AddRefreshDatesToCriteria(SearchCriteria searchCriteria)
	    {
	        return (new StringBuilder(searchCriteria.ToString(";"))).AppendFormat(GetDataRefreshDates()).ToString();
	    }

	    #endregion

	    #region Web Form Designer generated code

	    /// <summary>
	    /// Handler for the event raised when the page is being initialized.
	    /// Retrieves the cache policy in force.
	    /// </summary>
	    /// <param name="e"></param>
	    protected override void OnInit(EventArgs e)
	    {
	        InitializeComponent();
	        base.OnInit(e);

	        cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
	        pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];
	    }

	    /// <summary>
	    /// Required method for Designer support - do not modify
	    /// the contents of this method with the code editor.
	    /// </summary>
	    private void InitializeComponent()
	    {
	    }

	    #endregion
	}
}
